<i18n locale="zh-CN" lang="yaml">
Processing, please wait...: 正在处理中，请稍后...

Multiple Editing: 多重编辑
This page does not support multiple users or multiple tabs for editing at the same time, to avoid the possible problem of data overwriting each other, please confirm before operation: 本功能不支持多人或多窗口同时编辑。为避免可能出现的数据相互覆盖等问题，请确认后再进行操作

System Upgraded                                            : 系统已更新
System has been upgraded<br>and the page may be out of date: 系统已经升级，页面可能已经过时
Please refresh the page and continue                       : 请刷新页面后继续
Not Now                                                    : 等会再说
</i18n>

<!-- Generated by OpenCC START -->
<i18n locale="zh-HK" lang="yaml">
Multiple Editing: 多重編輯
Not Now: 等會再説
Please refresh the page and continue: 請刷新頁面後繼續
Processing, please wait...: 正在處理中，請稍後...
System Upgraded: 系統已更新
System has been upgraded<br>and the page may be out of date: 系統已經升級，頁面可能已經過時
? This page does not support multiple users or multiple tabs for editing at the same time, to avoid the possible problem of data overwriting each other, please confirm before operation
: 本功能不支持多人或多窗口同時編輯。為避免可能出現的數據相互覆蓋等問題，請確認後再進行操作
</i18n>
<i18n locale="zh-TW" lang="yaml">
Multiple Editing: 多重編輯
Not Now: 等會再說
Please refresh the page and continue: 請重新整理頁面後繼續
Processing, please wait...: 正在處理中，請稍後...
System Upgraded: 系統已更新
System has been upgraded<br>and the page may be out of date: 系統已經升級，頁面可能已經過時
? This page does not support multiple users or multiple tabs for editing at the same time, to avoid the possible problem of data overwriting each other, please confirm before operation
: 本功能不支援多人或多視窗同時編輯。為避免可能出現的資料相互覆蓋等問題，請確認後再進行操作
</i18n>
<!-- Generated by OpenCC END -->

<template>
  <div id="AppContainer"
    :element-loading-text="$t('Processing, please wait...')"
    element-loading-spinner="el-icon-loading"
    v-loading.fullscreen.body.lock="$store.getters.isProcessing">
    <div id="NoticeBar" v-if="showNoticeBar" :style="{ backgroundColor: $store.getters.SYSTEM_SETTINGS('NOTICE_BAR_COLOR') }">
      {{ $store.getters.SYSTEM_SETTINGS('NOTICE_BAR_TEXT') }}
    </div>
    <div id="Navi" v-if="showNavi">
      <Navi></Navi>
    </div>
    <div id="View" :style="{top: viewTop}">
      <router-view />
    </div>

    <el-dialog
      id="CompleteUserProfile"
      :title="$t('Complete User Profile for git committing')"
      :visible.sync="$store.state.showCompleteUserProfile"
      :show-close="false"
      :close-on-click-modal="false"
      :close-on-press-escape="false">
      <p class="text-main">
        {{ $t('Managing Script Market based on git requires your user NAME and EMAIL.') }}
        <br>{{ $t('This information will be used as the user information for git commits.') }}
      </p>
      <br>
      <el-form ref="form" label-width="115px" :model="form" :rules="formRules">
        <el-form-item :label="$t('User Name')" prop="name">
          <el-input
            maxlength="200"
            v-model="form.name"></el-input>
        </el-form-item>
        <el-form-item :label="$t('Email')" prop="email">
          <el-input
            maxlength="200"
            v-model="form.email"></el-input>
        </el-form-item>
      </el-form>

      <div slot="footer">
        <el-button size="small" @click="$store.commit('updateShowCompleteUserProfile', false)">{{ $t('Cancel') }}</el-button>
        <el-button size="small" type="primary" @click="updateUserProfile()">{{ $t('Save') }}</el-button>
      </div>
    </el-dialog>
    <el-dialog
      id="UpgradeNotice"
      :visible.sync="showUpgradeNotice"
      :show-close="false"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      top="15vh"
      :title="$t('System Upgraded')"
      width="600px">
      <div class="upgrade-notice">
        <div class="upgrade-notice-logo">
          <div>
            <Logo type="auto" height="60px"></Logo>
            <i class="fa fa-fw fa-magic fa-flip-horizontal upgrade-notice-logo-icon"></i>
          </div>
          <div class="upgrade-notice-version" v-if="T.notNothing($store.state.serverUpgradeInfo)">
            <span>{{ $store.state.serverUpgradeInfo.prev }}</span>
            <span>&nbsp;&#10132;&nbsp;</span>
            <span>{{ $store.state.serverUpgradeInfo.next }}</span>
          </div>
        </div>
        <p class="upgrade-notice-refresh">
          <span class="text-main" v-html="$t('System has been upgraded<br>and the page may be out of date')"></span>
          <br>
          <br>
          <span class="text-bad">{{ $t('Please refresh the page and continue') }}</span>
        </p>
        <el-button @click="showUpgradeNotice = false">{{ $t('Not Now') }}</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import 'element-ui/lib/theme-chalk/display.css';
import Navi from '@/components/Navi'

import { io } from 'socket.io-client';
import axios from 'axios';

export default {
  name: 'App',
  components: {
    Navi,
  },
  watch: {
    '$store.state.shortcutAction'(val) {
      switch(val.action) {
        case 'app.searchEveryThing':
          // TODO Ctrl + P for search every thing
          console.log('Ctrl + P Search Every Thing')
          break;
      }
    },
    $route: {
      immediate: true,
      handler(to, from) {
        if (to && to.name) {
          this.reportAndCheckClientConflict(true);
        }
      }
    },
    isSignedIn(val) {
      if (val) {
        // Socket.io auth
        this.socketIO.emit('auth', this.$store.state.xAuthToken);

        // Redirect
        if (this.$route.name === 'index') {
          this.$router.push({ name: 'intro' });
        }

      } else {
        // After sign-out
        if (this.$route.name !== 'sign-out') {
          location.href = '/';
        }
      }
    },
    uiTheme(val) {
      window._DFF_switchUITheme(val);
    },
    '$store.state.serverUpgradeInfo'(val) {
      if (this.T.notNothing(val)) {
        this.showUpgradeNotice = true;
      }
    },
  },
  methods: {
    reportAndCheckClientConflict(showNotice) {
      if (!this.$store.getters.isSocketIOReady) return;

      let routeName = null; // Route name
      let checkOnly = null; // Check only or not, do not record online status
      switch (this.$route.name) {
        case 'code-viewer':
          // The code view page only checks if the code edit page is locked
          routeName = 'code-editor';
          checkOnly  = true;
          break;

        default:
          // Checks if this page is locked by default
          routeName = this.$route.name;
          checkOnly = false;
          break;
      }

      let checkRouteInfo = {
        routeName  : routeName,
        routeQuery : this.$route.query,
        routeParams: this.$route.params,
        checkOnly  : checkOnly,
        conflictId : window.conflictId,
      };
      this.socketIO.emit('reportAndCheckClientConflict', checkRouteInfo, resData => {
        if (this.$store.getters.SYSTEM_INFO('MODE') === 'dev' && resData !== this.prevReportAndCheckClientConflictResData) {
          this.prevReportAndCheckClientConflictResData = resData;
        }
        resData = JSON.parse(resData);

        // Do nothing if no conflict
        if (!resData || !resData.data) return;

        let isConflict = resData.data.isConflict;

        // Record confliected route
        this.$store.commit('updateConflictedRoute', {
          routeInfo : this.$route,
          isConflict: isConflict,
          conflictId: resData.data.conflictId,
          user      : resData.data.user,
        });

        // Show notice if conflict occured
        if (isConflict && showNotice) {
          switch(this.$route.name) {
            case 'code-viewer':
            case 'code-editor':
              // The code view/edit page already has regular text prompts, so no additional prompts are needed.
              break;

            default:
              this.T.notify(`<span class="text-bad">${this.$t('Multiple Editing')}</span>
                <br>${this.$t('This page does not support multiple users or multiple tabs for editing at the same time, to avoid the possible problem of data overwriting each other, please confirm before operation')}`, 'warning');
              break;
          }
        }
      });
    },

    async updateUserProfile() {
      try {
        await this.$refs.form.validate();
      } catch(err) {
        return console.error(err);
      }

      let apiRes = await this.T.callAPI('post', '/api/v1/auth/profile/do/modify', {
        body    : { data: this.T.jsonCopy(this.form) },
        feedback: { okMessage: this.$t('User Profile saved') },
      });
      if (!apiRes || !apiRes.ok) return;

      this.$store.commit('updateShowCompleteUserProfile', false);
      this.$store.dispatch('loadUserProfile');
    },

    async checkNewVersion() {
      if (!this.T.parseVersion(this.$store.getters.SYSTEM_INFO('VERSION'))) return;

      try {
        let axiosOpt = {
          headers: { 'Cache-Control': 'no-cache' }
        };
        let resp = await axios.get('https://static.guance.com/dataflux-func/portable/version', axiosOpt);
        let latestVersion = ('' + resp.data).trim();
        if (this.T.parseVersion(latestVersion)) {
          this.$store.commit('updateLatestVersion', latestVersion);
        }
      } catch(_) {
        // Ignore errors
      }
    },
  },
  computed: {
    showNavi() {
      switch(this.$route.name) {
        case null:
        case 'index':
        case 'func-doc':
        case 'func-api-doc':
        case 'sign-out':
          return false;

        default:
          return true;
      }
    },
    showNoticeBar() {
      return this.$store.getters.SYSTEM_SETTINGS('NOTICE_BAR_ENABLED')
          && this.$store.getters.SYSTEM_SETTINGS('NOTICE_BAR_TEXT');
    },
    viewTop() {
      let top = 0;

      if (this.showNavi)      top += 30;
      if (this.showNoticeBar) top += 16;

      return `${top}px`;
    },
    isSignedIn() {
      return this.$store.getters.isSignedIn;
    },
    uiTheme() {
      return this.$store.getters.uiTheme;
    },
  },
  props: {
  },
  data() {
    let userProfile = this.$store.state.userProfile || {};

    return {
      prevReportAndCheckClientConflictResData: null,

      form: {
        name : userProfile.name  || '',
        email: userProfile.email || '',
      },
      formRules: {
        name: [
          {
            trigger : 'change',
            message : this.$t('Please input name'),
            required: true,
          },
        ],
        email: [
          {
            trigger : 'change',
            message : this.$t('Please input email'),
            required: true,
            pattern: this.C.RE_PATTERN.email,
          },
        ]
      },

      socketIOReportTimer: null,

      showUpgradeNotice: false,
    }
  },
  mounted() {
    // Init Socket.io and start page reporting timer
    const connectSocketIO = () => {
      if (this.$store.getters.isSocketIOReady) return;

      // SocketIO connect / auth
      if (this.isSignedIn) {
        this.socketIO.emit('auth', this.$store.state.xAuthToken);
      }
    }
    const handleError = err => {
      console.warn(err);

      try {
        err = JSON.parse(err);
        switch (err.reason) {
          case 'ESocketIOAuth':
            this.$store.commit('updateSocketIOStatus', false);
            connectSocketIO();
            break;
        }

      } catch(err) {
        console.warn(err);
      }
    }
    const handleDisconnect = () => {
      setTimeout(() => {
        this.socketIO.connect();
      }, 1000)
    }
    const handleAuth = () => {
      this.$store.commit('updateSocketIOStatus', true);
    }

    this.$nextTick(() => {
      let ioPath = `${this.T.getVirtualDir()}/socket.io/`;
      let ioURL  = process.env.VUE_APP_BACKEND_SERVER || '/';

      this.socketIO = window.socketIO = io(ioURL, {
        path: ioPath,
        // This is required, otherwise Socket.io will cause a “400: Session ID unknown” error with a multi-pod deployment.
        transports: [ 'websocket', 'polling' ],
      });
      this.socketIO
        .on('error', handleError)
        .on('connect', connectSocketIO)
        .on('reconnect', connectSocketIO)
        .on('disconnect', handleDisconnect)
        .on('auth.resp', handleAuth)

      // Report current page regularly
      this.socketIOReportTimer = setInterval(() => {
        connectSocketIO();
        this.reportAndCheckClientConflict();
      }, 1 * 1000);
    });

    // listen for shortcuts
    setImmediate(() => {
      var app = this;

      document.onkeydown = function(e) {
        function shortcutAction(action) {
          e.preventDefault();
          app.$store.commit('updateShortcutAction', {
            action   : action,
            timestamp: app.T.getTimestamp(),
          });
        }

        const ctrlOrCmd = app.T.isMac() ? e.metaKey : e.ctrlKey;
        const ctrl      = e.ctrlKey;
        const alt       = e.altKey;
        const shift     = e.shiftKey;

        const key_1 = e.keyCode === 49;
        const key_2 = e.keyCode === 50;
        const key_3 = e.keyCode === 51;
        const key_B = e.keyCode === 66;
        const key_E = e.keyCode === 69;
        const key_F = e.keyCode === 70;
        const key_P = e.keyCode === 80;
        const key_S = e.keyCode === 83;

        // Global Ctrl + P: Search
        if (ctrl && key_P) {
          return shortcutAction('app.searchEveryThing');
        }

        // Code Editor Ctrl/Command + S: Save
        if (app.$route.name === 'code-editor' && ctrlOrCmd && key_S) {
          return shortcutAction('codeEditor.save');
        }

        // Code Editor Ctrl/Command + B: Run
         if (app.$route.name === 'code-editor' && ctrlOrCmd && key_B) {
          return shortcutAction('codeEditor.run');
        }

        // Code Viewer Ctrl/Command + 1: Show draft
        if (app.$route.name === 'code-viewer' && ctrlOrCmd && key_1) {
          return shortcutAction('codeViewer.showDraft');
        }

        // Code Viewer Ctrl/Command + 2: Show published
        if (app.$route.name === 'code-viewer' && ctrlOrCmd && key_2) {
          return shortcutAction('codeViewer.showPublished');
        }

        // Code Viewer Ctrl/Command + 3: Show diff
        if (app.$route.name === 'code-viewer' && ctrlOrCmd && key_3) {
          return shortcutAction('codeViewer.showDiff');
        }

        // Code Viewer Ctrl/Command + E: Enter code editor
        if (app.$route.name === 'code-viewer' && ctrlOrCmd && key_E) {
          return shortcutAction('codeViewer.enterEditor');
        }

      };
    });

    // Check for updates
    this.checkNewVersion();
  },
  beforeDestroy() {
    if (this.socketIOReportTimer) clearInterval(this.socketIOReportTimer);
  },
}
</script>

<style scoped>
.upgrade-notice {
  text-align: center;
}
.upgrade-notice-refresh {
  font-size: 18px;
  padding: 20px 0;
}
.upgrade-notice-logo {
  display: flex;
  align-items: center;
  flex-direction: column;
}
.upgrade-notice-logo-icon {
  position: absolute;
  font-size: 45px;
  top: 55px;
  right: 120px;
  color: var(--primary);
}
.upgrade-notice-arrow {
  margin: auto 10px;
}
.upgrade-notice-version {
  font-size: 16px;
  color: var(--primary);
  margin-top: 10px;
}
#NoticeBar {
  background-color: var(--primary-dark);
  font-size: 12px;
  padding: 1px;
  text-align: center;
  color: white;
  height: 16px;
  z-index: 2000;
  position: relative;
}
#Navi {
  border-bottom: 1px solid var(--border);
  position: relative;
  height: 30px;
  z-index: 2000;
}
#View {
  position: absolute;
  top: 0;
  bottom: 0;
  min-width: 1200px;
  width: 100%;
}
#CompleteUserProfile p {
  padding: 10px 30px;
  text-align: center;
}
#CompleteUserProfile > .el-dialog {
  width: 620px;
}
</style>

<style>
</style>
